package com.hys.app.service.erp.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.hys.app.converter.erp.WarehouseEntryBatchConverter;
import com.hys.app.framework.database.WebPage;
import com.hys.app.framework.database.mybatisplus.base.BaseServiceImpl;
import com.hys.app.framework.exception.ServiceException;
import com.hys.app.mapper.erp.WarehouseEntryBatchMapper;
import com.hys.app.model.erp.dos.ProductDO;
import com.hys.app.model.erp.dos.StockBatchFlowDO;
import com.hys.app.model.erp.dos.WarehouseDO;
import com.hys.app.model.erp.dos.WarehouseEntryBatchDO;
import com.hys.app.model.erp.dto.StockUpdateDTO;
import com.hys.app.model.erp.dto.WarehouseEntryBatchQueryParams;
import com.hys.app.model.erp.enums.StockChangeSourceEnum;
import com.hys.app.model.erp.enums.StockOperateEnum;
import com.hys.app.model.erp.vo.WarehouseEntryBatchVO;
import com.hys.app.service.erp.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.hys.app.framework.util.CollectionUtils.*;

/**
 * 入库批次业务层实现
 *
 * @author 张崧
 * @since 2023-12-08 11:49:52
 */
@Service
public class WarehouseEntryBatchManagerImpl extends BaseServiceImpl<WarehouseEntryBatchMapper, WarehouseEntryBatchDO> implements WarehouseEntryBatchManager {

    @Autowired
    private WarehouseEntryBatchConverter converter;

    @Autowired
    private ProductStockManager productStockManager;

    @Autowired
    private StockBatchFlowManager stockBatchFlowManager;

    @Autowired
    private WarehouseManager warehouseManager;

    @Autowired
    private ProductManager productManager;

    @Override
    public WebPage<WarehouseEntryBatchVO> list(WarehouseEntryBatchQueryParams queryParams) {
        WebPage<WarehouseEntryBatchDO> webPage = baseMapper.selectPage(queryParams);
        List<ProductDO> productList = null;
        if (Boolean.TRUE.equals(queryParams.getQueryProduct())) {
            List<Long> productIds = convertList(webPage.getData(), WarehouseEntryBatchDO::getProductId);
            productList = productManager.listByIds(productIds);
        }
        return converter.convert(webPage, productList);
    }

    @Override
    public Map<Long, List<WarehouseEntryBatchDO>> listAvailableBatch(Long warehouseId, List<Long> productIds) {
        List<WarehouseEntryBatchDO> list = lambdaQuery()
                .eq(WarehouseEntryBatchDO::getWarehouseId, warehouseId)
                .in(WarehouseEntryBatchDO::getProductId, productIds)
                .gt(WarehouseEntryBatchDO::getRemainNum, 0)
                .orderByAsc(WarehouseEntryBatchDO::getEntryTime)
                .list();
        return convertMultiMap(list, WarehouseEntryBatchDO::getProductId);
    }

    @Override
    public List<WarehouseEntryBatchDO> listByWarehouseEntryIds(Collection<Long> warehouseEntryIds) {
        if (CollUtil.isEmpty(warehouseEntryIds)) {
            return Collections.emptyList();
        }
        return lambdaQuery().in(WarehouseEntryBatchDO::getWarehouseEntryId, warehouseEntryIds).list();
    }

    @Override
    public List<WarehouseEntryBatchVO> listAll(WarehouseEntryBatchQueryParams queryParams) {
        return converter.convert(baseMapper.listAll(queryParams));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(StockChangeSourceEnum sourceType, String sourceSn, List<WarehouseEntryBatchDO> batchList) {
        // 1.保存入库批次
        batchList.forEach(warehouseEntryBatchDO -> warehouseEntryBatchDO.setCreateSource(sourceType));
        super.saveBatch(batchList);

        // 2.同步更新仓库中的库存
        // 2.1按仓库id+商品id分组，减少循环更新次数
        Map<String, List<WarehouseEntryBatchDO>> groupBy = batchList.stream().collect(Collectors.groupingBy(batchDO -> batchDO.getWarehouseId() + "_" + batchDO.getProductId()));
        // 2.2循环增加库存
        for (List<WarehouseEntryBatchDO> batchGroupList : groupBy.values()) {
            WarehouseEntryBatchDO batchDO = batchGroupList.get(0);
            // 将多个批次的库存数量合并
            int totalNum = batchGroupList.stream().mapToInt(WarehouseEntryBatchDO::getEntryNum).sum();
            // 更新
            productStockManager.updateStock(batchDO.getWarehouseId(), batchDO.getProductId(), StockOperateEnum.Increase, totalNum);
        }

        // 3.生成库存流水
        List<StockBatchFlowDO> flowList = converter.convertFlowList(sourceType, sourceSn, batchList);
        fillDept(flowList);
        stockBatchFlowManager.saveBatch(flowList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStock(StockChangeSourceEnum sourceType, String sourceSn, List<StockUpdateDTO> updateStockList) {
        // 1.更新批次的库存
        for (StockUpdateDTO stockUpdateDTO : updateStockList) {
            innerUpdateStock(stockUpdateDTO.getBatchId(), stockUpdateDTO.getTip(), stockUpdateDTO.getOperate(), stockUpdateDTO.getChangeNum());
        }

        // 2.同步更新仓库中的库存
        // 2.1先查询一下批次信息
        List<Long> ids = convertList(updateStockList, StockUpdateDTO::getBatchId);
        List<WarehouseEntryBatchDO> batchList = super.listByIds(ids);
        Map<Long, WarehouseEntryBatchDO> batchMap = convertMap(batchList, WarehouseEntryBatchDO::getId, Function.identity());
        // 2.2循环更新仓库中的库存
        for (StockUpdateDTO stockUpdateDTO : updateStockList) {
            WarehouseEntryBatchDO batchDO = batchMap.get(stockUpdateDTO.getBatchId());
            // 更新库存
            productStockManager.updateStock(batchDO.getWarehouseId(), batchDO.getProductId(), stockUpdateDTO.getOperate(), stockUpdateDTO.getChangeNum());
        }

        // 3.生成库存流水
        List<StockBatchFlowDO> flowList = converter.convertFlowList(sourceType, sourceSn, batchMap, updateStockList);
        fillDept(flowList);
        stockBatchFlowManager.saveBatch(flowList);
    }

    @Override
    public Map<Long, Integer> queryRemainStockNum(List<Long> ids) {
        return lambdaQuery()
                .in(WarehouseEntryBatchDO::getId, ids)
                .list()
                .stream().collect(Collectors.toMap(WarehouseEntryBatchDO::getId, WarehouseEntryBatchDO::getRemainNum));
    }

    private void innerUpdateStock(Long batchId, String tip, StockOperateEnum operate, Integer changeNum) {
        if (changeNum <= 0) {
            throw new ServiceException("库存变更数量必须大于0");
        }

        boolean update = lambdaUpdate()
                .setSql("remain_num = remain_num + " + (operate == StockOperateEnum.Increase ? changeNum : -changeNum))
                .eq(WarehouseEntryBatchDO::getId, batchId)
                // 如果是扣减库存，需要校验剩余库存是否充足
                .ge(operate == StockOperateEnum.Reduce, WarehouseEntryBatchDO::getRemainNum, changeNum)
                .update();

        if (!update) {
            String msg = operate == StockOperateEnum.Increase ?
                    StrUtil.format("【{}】不存在", tip) :
                    StrUtil.format("【{}】剩余库存不足", tip);
            throw new ServiceException(msg);
        }
    }


    private void fillDept(List<StockBatchFlowDO> flowList) {
        List<WarehouseDO> warehouseList = warehouseManager.listByIds(convertList(flowList, StockBatchFlowDO::getWarehouseId));
        Map<Long, Long> warehouseDeptMap = convertMap(warehouseList, WarehouseDO::getId, WarehouseDO::getDeptId);
        for (StockBatchFlowDO stockBatchFlowDO : flowList) {
            stockBatchFlowDO.setDeptId(warehouseDeptMap.get(stockBatchFlowDO.getWarehouseId()));
        }
    }
}

